#!/usr/bin/python
#-*-coding: utf-8 -*-

"""This is the main module of Aangel, the Interop Router scheduller.

Aangel monitors the database looking for jobs without a clusterID, and calculates
the total of resources in the IR's cluster network. So, it divides the jobs
among the clusters using a specific criterion.

In the version 1.0 (IR 3.3), this criterion is quite simple and considers 
only the free resources of the active cluster, without taking into 
account other factors.
"""

# para melhorar a conexão com o BD
from connection import Connection
# parametros da conexao com o banco de dados
from config import configs

__version__ = '1.0'

class Aangel(object):
    """This classes implements the Aangel's operations."""
      
    def getTotalResources(self, clusters):
        """This method calculates the total of clusters resources in 
        the Interop Router's network.
        
        clusters is a list of tuples with cluster id and its free resources. 
        
        """
        
        total = 0
        
        for k in clusters:      # k eh uma tuple (id, workLoad)
            total += k[1]
        
        return total
    
    def divide(self, clusters, jobs, resources):
        """This is the method that implements the scheduing. 
        It distributes a cluster ID for each job, until the end jobs
        or no more sufficient resources.
        
        jobs is a list where each element is a tuple with the job id and the
        number of processors required by job.
        
        clusters is a list of tuples with cluster id and its free resources. 
        
        resources is a total resources of IR's network.
        
        """
        
        # aqui, pode-se ordenar a lista de jobs
        # para dar preferencia para jobs menores ou mais antigos
        # por exemplo
        
        result = []
              
        while resources > 0 and len(jobs) > 0:
            for c in clusters:
                
                # se os jobs acabaram, nao ha mais o que fazer
                if not jobs: break
                
                job = jobs[0]
                if job[1] <= c[1]:                  # job.processors <= cluster.workLoad
                    result.append((job[0], c[0]))   # (jobId, clusterId) 
                    jobs.remove(job)                # remove o job jah escalonado
                    resources -= job[1]             # resources -= job.processors
                    
            # se nao conseguiu colocar o job em cluster nenhum, move ele pro fim da lista
            if jobs: 
                jobs.append(jobs.pop(0))
        
        return result
            

    # esse metodo eh onde a coisa toda acontece
    def work(self, connection):
        """This method realizes the Aangel's task. 
        
        It captures the jobs to allocata,
        verify if there are free resources and distribute the jobs among clusters.
        
        In the end, it send the results to database, changing the job id to the
        new ids.
        
        connection is a Connection object, from connectSQL module.
        
        """
        
        # verifica os resursos e os jobs a escalonar
        jobs = connection.getJobs()
        clusters = connection.getClusters()
        
        # caso nao haja recursos ou
        if not jobs or not clusters:
            return
        
        resources = self.getTotalResources(clusters)
        
        # cria uma lista com cada job e o cluster pra onde ele vai
        jobList = self.divide(clusters, jobs, resources)
        
        # faz as mudancas na base de dados
        connection.sendResults(jobList)
        
        print 'Done\n'
        print 'Waiting for jobs...'
        
            
if __name__ == "__main__":
    print "Interop Router Aangel, version %s\n" %(__version__)
    
    e = Aangel()
    
    # Connect to the Web Server
    connection = Connection(configs)
    
    print 'Waiting for jobs...'
    while 1:
        e.work(connection)
